package com.yxx.service.Impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yxx.constant.CommonConstant;
import com.yxx.entity.Menu;
import com.yxx.entity.RoleMenu;
import com.yxx.entityModel.vo.query.MenuQuery;
import com.yxx.entityModel.vo.request.MenuRequest;
import com.yxx.entityModel.vo.response.MenuOptionResponse;
import com.yxx.entityModel.vo.response.MenuResponse;
import com.yxx.entityModel.vo.response.MenuTreeResponse;
import com.yxx.mapper.MenuMapper;
import com.yxx.mapper.RoleMenuMapper;
import com.yxx.service.MenuService;
import com.yxx.utils.BeanCopyUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yuxiangxun
 * @date 2025/1/22
 * @apiNote
 */
@Service
@Slf4j
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
    @Resource
    private MenuMapper menuMapper;

    @Resource
    private RoleMenuMapper roleMenuMapper;

    /**
     * 查询菜单列表
     * @param menuQuery
     * @return
     */
    @Override
    public List<MenuResponse> listMenuVO(MenuQuery menuQuery) {
        // 查询当前所有菜单列表
        List<MenuResponse> menuResponseList = menuMapper.selectMenuVOList(menuQuery);
        // 当前菜单id列表
        // 先将List<MenuResponse> 转化为 Stream<MenuResponse> 将每个MenuResponse对象映射为其id字段(Integer类型)，将Stream<Integer>收集为一个Set<Integer>
        Set<Integer> menuIdList = menuResponseList.stream()
                .map(MenuResponse::getId)
                .collect(Collectors.toSet());
        return menuResponseList.stream().map(menuVO -> {
                    Integer parentId = menuVO.getParentId();
                    // parentId不在当前菜单id列表，说明为父级菜单id，根据此id作为递归的开始条件节点
                    if (!menuIdList.contains(parentId)) {
                        menuIdList.add(parentId);
                        return recurMenuList(parentId, menuResponseList);
                    }
                    return new ArrayList<MenuResponse>();
                })
                .collect(
                        ArrayList::new,
                        ArrayList::addAll,
                        ArrayList::addAll
                );
    }

    /**
     * 递归生成菜单列表
     *
     * @param parentId 父菜单id
     * @param menuList 菜单列表
     * @return 菜单列表
     */
    private List<MenuResponse> recurMenuList(Integer parentId, List<MenuResponse> menuList) {
        return menuList.stream()
                .filter(menuVO -> menuVO.getParentId().equals(parentId))
                .peek(menuVO -> menuVO.setChildren(recurMenuList(menuVO.getId(), menuList)))
                .collect(Collectors.toList());
    }





    /**
     * 添加菜单
     * @param menuRequest
     */
    @Override
    public void addMenu(MenuRequest menuRequest) {
        // 查询请求中的菜单名称对应的菜单信息是否存在
        Menu existMenu = menuMapper.selectOne(new LambdaQueryWrapper<Menu>().select(Menu::getId).eq(Menu::getMenuName, menuRequest.getMenuName()));
        // 断言
        Assert.isNull(existMenu, menuRequest.getMenuName()+"菜单已存在");
        // 查询请求中的菜单名称对应的菜单信息不存在，将menuRequest对象转换为Menu对象，并插入数据库
        Menu newMenu = BeanCopyUtils.copyBean(menuRequest, Menu.class);
        baseMapper.insert(newMenu);
    }

    /**
     * 删除菜单
     * @param menuId
     */
    @Override
    public void deleteMenu(Integer menuId) {
        // 查询菜单id对应的菜单信息是否存在子菜单(查询子菜单数量)
        Long menuChildrenCount = menuMapper.selectCount(new LambdaQueryWrapper<Menu>().eq(Menu::getParentId, menuId));
        // 断言 如果存在子菜单的话，无法删除该菜单
        Assert.isFalse(menuChildrenCount > 0 , "存在子菜单，无法删除");
        // 如果没有存在子菜单，查看菜单是否已分配给某个角色
        Long roleMenuCount = roleMenuMapper.selectCount(new LambdaQueryWrapper<RoleMenu>().eq(RoleMenu::getMenuId, menuId));
        // 如果该菜单有涉及到角色的话，无法删除该菜单
        Assert.isFalse(roleMenuCount > 0, "该菜单已分配对应的角色，无法删除");
        // 删除菜单 （没有子菜单且该菜单没有分配给某个角色）
        menuMapper.deleteById(menuId);
    }

    /**
     * 修改菜单
     * @param menuRequest
     */
    @Override
    public void updateMenu(MenuRequest menuRequest) {
        // 这里判断一下菜单请求中的菜单信息是否与数据库中的菜单信息是否一致，一致的话，无法修改（无法通过断言）
        Menu existMenu = menuMapper.selectOne(new LambdaQueryWrapper<Menu>()
                        .select(Menu::getId)
                        .eq(Menu::getMenuName, menuRequest.getMenuName()));
        Assert.isFalse(!existMenu.getId().equals(menuRequest.getId()) && Objects.nonNull(existMenu),menuRequest.getMenuName()+"菜单已存在");
        //请求中的菜单id与数据库中的菜单id相同且不为空，则更新数据库
        // 对象转换，将menuRequest对象转换为Menu对象，并更新数据库
        Menu newMenu = BeanCopyUtils.copyBean(menuRequest, Menu.class);
        baseMapper.updateById(newMenu);
    }






    /**
     * 获取菜单下拉树
     * @return
     */
    @Override
    public List<MenuTreeResponse> menuListTree() {
        List<MenuTreeResponse> menuTreeResponseList = menuMapper.selectMenuTree();
        // 递归生成子菜单树并封装菜单下拉树
        return recurMenuTreeList(CommonConstant.PARENT_ID,menuTreeResponseList);
    }

    /**
     * 递归生成菜单下拉树
     * @param parentId
     * @param menuTreeResponseList
     * @return
     */
    private List<MenuTreeResponse> recurMenuTreeList(Integer parentId, List<MenuTreeResponse> menuTreeResponseList) {
        return menuTreeResponseList.stream()
                //过滤出所有parentId等于传入的parentId的菜单项。这些菜单项将作为当前父节点的子节点
                .filter(menu -> menu.getParentId().equals(parentId))
                //对每个过滤出的菜单项，递归调用recurMenuTreeList方法，以该菜单项的id作为新的父节点ID，继续查找其子节点，并将结果设置为该菜单项的children属性
                .peek(menu -> menu.setChildren(recurMenuTreeList(menu.getId(), menuTreeResponseList)))
                .collect(Collectors.toList());
    }





    /**
     *  获取菜单选项树
     * @return
     */
    @Override
    public List<MenuOptionResponse> menuOptionList() {
        List<MenuOptionResponse> menuOptionResponseList = menuMapper.selectMenuOptions();
        return recurMenuOptionList(CommonConstant.PARENT_ID,menuOptionResponseList);
    }



    /**
     * 递归生成菜单选项树
     * @param parentId
     * @param menuOptionResponseList
     * @return
     */
    private List<MenuOptionResponse> recurMenuOptionList(Integer parentId, List<MenuOptionResponse> menuOptionResponseList) {
        return menuOptionResponseList.stream()
                .filter(menu -> menu.getParentId().equals(parentId))
                .peek(menu -> menu.setChildren(recurMenuOptionList(menu.getValue(), menuOptionResponseList)))
                .collect(Collectors.toList());
    }


    /**
     * 编辑菜单
     * @param menuId
     * @return
     */
    @Override
    public MenuRequest editMenu(Integer menuId) {
        return menuMapper.selectMenuById(menuId);
    }


}
